#ifndef __MSG_QUEUE_H__
#define __MSG_QUEUE_H__

template <class TMsg>
class MsgQueue
{
public:
	MsgQueue(unsigned int size)
	{
		m_queue = new TMsg[size];
		m_size = size;
		m_headpos = 0;
		m_tailpos = 0;
		m_isempty = true;
		m_isfull = false;

		::InitializeCriticalSection(&m_csThreadSafe);
	}

	~MsgQueue()
	{
		delete[] m_queue;
	}

	inline void Push(TMsg& msg)
	{
		if (m_isfull)
		{
			return;
		}

		::EnterCriticalSection(&m_csThreadSafe);

		memcpy(m_queue + m_tailpos, &msg, sizeof(msg));

		if (m_tailpos == m_size - 1)
		{
			m_tailpos = 0;
		}
		else
		{
			m_tailpos++;
		}

		m_isfull = !m_isempty && m_tailpos == m_headpos;
		m_isempty = false;

		::LeaveCriticalSection(&m_csThreadSafe);
	}

	inline TMsg* Pop()
	{
		static TMsg msg;

		if (m_isempty)
		{
			return NULL;
		}

		::EnterCriticalSection(&m_csThreadSafe);

		memcpy(&msg, m_queue + m_headpos, sizeof(msg));

		if (m_headpos == m_size - 1)
		{
			m_headpos = 0;
		}
		else
		{
			m_headpos++;
		}

		m_isempty = !m_isfull && m_headpos == m_tailpos;
		m_isfull = false;

		::LeaveCriticalSection(&m_csThreadSafe);

		return &msg;
	}

	//clear the quene and pop the last one
	inline TMsg* PopAndClear()
	{
		static TMsg msg;

		if (m_isempty)
		{
			return NULL;
		}

		::EnterCriticalSection(&m_csThreadSafe);

		if (m_tailpos == 0)
		{
			m_tailpos = m_size - 1;
		}

		memcpy(&msg, m_queue + m_tailpos - 1, sizeof(msg));

		m_headpos = 0;
		m_tailpos = 0;

		m_isempty = true;
		m_isfull = false;

		::LeaveCriticalSection(&m_csThreadSafe);

		return &msg;
	}

private:
	TMsg* m_queue;
	unsigned int m_size;
	unsigned int m_headpos;
	unsigned int m_tailpos;

	bool m_isempty;
	bool m_isfull;

	CRITICAL_SECTION m_csThreadSafe;
};

#endif